package com.gebogebo.android.distancecalc;

import static java.lang.String.format;

import java.text.SimpleDateFormat;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.gebogebo.android.distancecalc.DistanceCalculatorService.DistanceServiceBinder;
import com.gebogebo.android.distancecalc.DistanceCalcReport;
import com.gebogebo.android.distancecalc.DistanceCalculatorUtilities;
import com.gebogebo.android.distancecalc.R;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;

/**
 * Launcher activity for distance calculator app
 * 
 * @author viraj
 */
public class DistanceCalculatorActivity extends MapActivity implements OnClickListener, DistanceCalculatorServiceListener {
    private static final String DATE_FORMAT_NOW = "dd-MMM-yyyy HH:mm";
    private static final int GPS_NOT_ENABLED_START = 1224231;
    private static final int GPS_NOT_ENABLED_RESUME = 324354;
    private static final int GPS_ENABLE_ACTIVITY_START = 1;
    private static final int GPS_ENABLE_ACTIVITY_RESUME = 2;
    private static final int DISTANCE_CALC_PREF_CHANGE = 3;
    
	private static String currentSpeedFormat = null;
	/*
	 * isCalculatingDistance = false and isPaused = false : "Start Calculating" and "Pause"
	 * isCalculatingDistance = true and isPaused = false : "Stop Calculating" and "Pause"
	 * isCalculatingDistance = true and isPaused = true : "Stop Calculating" and "Resume"
	 */
    private boolean isCalculatingDistance = false; //indicates state of app (calculating distance or not)
    private boolean isPaused = false;
    private DistanceCalculatorService locationService = null;
    private String hoursStr = null;
    private String timeElapsedStr = null;
    
    private TextView distanceText = null;
    private TextView speedText = null;
    private TextView timeText = null;
    private TextView errorText = null;
    
    private float multiplier = 1.0F;
    private float previousSpeed = 0.0F;
    private String distanceSuffix;

    /**
     * set activity variable as per its current operation
     */
    private void setActivityState() {
        Button button = (Button)findViewById(R.drawable.button);
        if(isCalculatingDistance) {
            // if app was brought to distance calculating mode
            //reset everything. don't worry it will get set if service was running in background
            button.setText(R.string.stop);
            speedText.setText(R.string.dots);
            distanceText.setText(R.string.empty);
            timeText.setText(R.string.empty);
            float actualDistance = locationService.getCurrentDistance();
            if(actualDistance >= 0.0) {
                long timeElapsed = locationService.getTimeElapsed();
                String visualDistance = DistanceCalculatorUtilities.getVisualDistance(actualDistance, timeElapsed, multiplier, distanceSuffix, hoursStr);
                distanceText.setText(visualDistance);
                errorText.setText(R.string.empty);
                timeText.setText(DistanceCalculatorUtilities.getVisualTime(timeElapsed, timeElapsedStr));
            } else {
                // it means that we are trying to find location of user
                errorText.setText(R.string.service_temp_not_available);
            }
            SharedPreferences defPref = PreferenceManager.getDefaultSharedPreferences(this);
            String action = defPref.getString(DistanceCalculatorPrefActivity.PREF_KEY_ACTION, 
            		DistanceCalculatorPrefActivity.PREF_BIKING);
            MapView map = (MapView)findViewById(R.id.mapview);
            map.getController().setZoom(DistanceCalculatorPrefActivity.getMinZoomLevelForAction(action));
            Log.i("activity", "handled on click event for button. calculating distance now.");
        } else {
            // if app was brought to non distance calculating mode
            button.setText(R.string.start);
            Button pauseButton = (Button) findViewById(R.drawable.pause);
            pauseButton.setText(R.string.pause);
            errorText.setText(R.string.empty);
            Log.i("activity", "handled on click event for button. not calculating distance now.");
        }
    }
    
    /**
     * sets distance calculating parameters as per chosen settings
     */
    private void setDistanceParameters() {
        SharedPreferences defPref = PreferenceManager.getDefaultSharedPreferences(this);
        //default selection is miles
        String selectedMetricId = defPref.getString(DistanceCalculatorPrefActivity.PREF_KEY_DISTANCE_UNIT, 
        		DistanceCalculatorPrefActivity.PREF_MILES);
        Log.i("activity", "got selected metric: " + selectedMetricId);
        multiplier = DistanceCalculatorPrefActivity.getDistanceMultiplierForDistanceUnit(selectedMetricId);
        if (DistanceCalculatorPrefActivity.PREF_KM.equals(selectedMetricId)) {
            distanceSuffix = getString(R.string.km);
            Log.i("menu", "KMs selected: " + distanceSuffix);
        } else if (DistanceCalculatorPrefActivity.PREF_MILES.equals(selectedMetricId)) {
            distanceSuffix = getString(R.string.miles);
            Log.i("menu", "Miles selected: " + distanceSuffix);
        }
    }
    
    private ServiceConnection serviceConn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i("serviceConn", "Service disconnected to activity");
        }
        
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i("serviceConn", "Service connected to activity");
            locationService = ((DistanceServiceBinder)service).getService();
            float currDistance = locationService.getCurrentDistance();
            if(currDistance < 0.0) {
                isCalculatingDistance = false;
            } else {
                isCalculatingDistance = true;
            }
            setActivityState();
        }
    };
    
    //DistanceCalculatorServiceListener methods
    @Override
    public void onDistanceChange(float newDistanceInMeters, long totalTimeInSecs, Location newLocation) {
        errorText.setText(R.string.empty);
        Log.i("serviceListener", "distance changed");
        String visualDistance = DistanceCalculatorUtilities.getVisualDistance(newDistanceInMeters, totalTimeInSecs, multiplier, distanceSuffix, hoursStr);
        distanceText.setText(visualDistance);
        
        int locationMarker = R.drawable.marker;
        if(newLocation.hasSpeed()) {
            if(previousSpeed != 0.0) {
            	if(newLocation.getSpeed() > DistanceCalculatorPrefActivity.getAccelerationMultiplier() * previousSpeed) {
            		//if new speed is more than previous speed, it is acceleration
            		locationMarker = R.drawable.marker_acc;
            	} else if (newLocation.getSpeed() < DistanceCalculatorPrefActivity.getDeaccelerationMultiplier() * previousSpeed) {
            		//if new speed is less than previous speed, it is deacceleration
            		locationMarker = R.drawable.marker_deacc;
            	}
            }
            timeText.setText(DistanceCalculatorUtilities.getVisualTime(totalTimeInSecs, timeElapsedStr));
        	String speed = DistanceCalculatorUtilities.getVisualCurrentSpeed(newLocation.getSpeed(), multiplier, distanceSuffix, hoursStr, currentSpeedFormat);
        	speedText.setText(speed);
        	previousSpeed = newLocation.getSpeed();
        }

        DistanceCalculatorUtilities.addLocationToMap(newLocation, getResources().getDrawable(locationMarker), 
    			(MapView)findViewById(R.id.mapview));
    }
    
    @Override
    public void onServiceStatusChange(int errorCode) {
        int textId = DistanceCalculatorUtilities.getErrorTextId(errorCode);
        if(textId > 0) {
            errorText.setText(textId);
        }
    }
    
    //activity overrides
    @Override
    public void onCreate(Bundle savedInstanceState) {
        DistanceCalculatorUtilities.deleteTempFiles();
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Log.i("activity", "set activity as content view: " + this);
        
        MapView map = (MapView)findViewById(R.id.mapview);
        map.setBuiltInZoomControls(true);
        map.setSatellite(false);

        SharedPreferences defPref = PreferenceManager.getDefaultSharedPreferences(this);
        String action = defPref.getString(DistanceCalculatorPrefActivity.PREF_KEY_ACTION, 
        		DistanceCalculatorPrefActivity.PREF_BIKING);
        map.getController().setZoom(DistanceCalculatorPrefActivity.getMinZoomLevelForAction(action));

        distanceText = (TextView) findViewById(R.drawable.distance);
        speedText = (TextView) findViewById(R.drawable.currentSpeed);
        timeText = (TextView) findViewById(R.drawable.timeElapsed);
        errorText = (TextView) findViewById(R.drawable.errors);
        hoursStr = getString(R.string.Hr);
        timeElapsedStr = getString(R.string.timeElapsedFormat);
        currentSpeedFormat = getString(R.string.currSpeedFormat);
        Button button = (Button)findViewById(R.drawable.button);
        Button pauseButton = (Button) findViewById(R.drawable.pause);
        button.setOnClickListener(this);
        pauseButton.setOnClickListener(this);
        Log.i("activity", "added button click listener");
        
        setDistanceParameters();
      
        Intent serviceStart = new Intent(Intent.ACTION_MAIN);
        serviceStart.setClassName("com.gebogebo.android.distancecalc", "com.gebogebo.android.distancecalc.DistanceCalculatorService");
        startService(serviceStart);
        bindService(serviceStart, serviceConn, BIND_AUTO_CREATE);
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        Log.i("menu", "Options menu created");
        return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if(item.getItemId() == R.id.menu_exit) {
            Log.i("menu", "Exit selected");
            DistanceCalculatorUtilities.deleteTempFiles();
            this.finish();
        } else if(item.getItemId() == R.id.settings) {
            Log.i("menu", "Settings selected");
            Intent prefIntent = new Intent(Intent.ACTION_EDIT);
            prefIntent.addCategory(Intent.CATEGORY_PREFERENCE);
            startActivityForResult(prefIntent, DISTANCE_CALC_PREF_CHANGE);         
        } else if(item.getItemId() == R.id.share) {
            Log.i("menu", "Share with selected");
            Intent intent = new Intent(Intent.ACTION_SEND);
            intent.setType("image/png");
            String tmpFilename =  DistanceCalculatorUtilities.saveParentView(distanceText.getRootView(), this, true);
            intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + tmpFilename));
            startActivity(Intent.createChooser(intent, getString(R.string.share)));
        } else if(item.getItemId() == R.id.help) {
        	Log.i("menu", "Help selected");
        	Intent helpIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://distancecalculator.gebogebo.com/help"));
        	startActivity(helpIntent);
        } else if(item.getItemId() == R.id.menu_capture) {
            String filename = DistanceCalculatorUtilities.saveParentView(distanceText.getRootView(), this, false);
            if(filename != null) {
                Toast.makeText(this, String.format(this.getString(R.string.msg_file_saved), filename), Toast.LENGTH_LONG).show();
            }
        	Log.i("activity", "successfully captured screenshot");
        } else if (item.getItemId() == R.id.menu_report) {
            DistanceCalcReport report = getSummaryReport();
            startReportActivity(report, false);
        }
        return true;
    }
    
    @Override
    public void onDestroy() {
        Log.i("lifecycle", "in destroy");
        if(locationService != null) {
        	locationService.removeListener(this);
        }
        
        if(isFinishing()) {
            Log.i("lifecycle", "activity is destory-finishing");
            //if acticity was 
            Intent serviceStop = new Intent(Intent.ACTION_MAIN);
            serviceStop.setClassName("com.gebogebo.android.distancecalc", "com.gebogebo.android.distancecalc.DistanceCalculatorService");
            unbindService(serviceConn);
            stopService(serviceStop);
        }
        super.onDestroy();
    }
    
    @Override
    public void onBackPressed() {
        Log.i("activity", "BACK button pressed");
        moveTaskToBack(false);
    }
    
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	    Log.i("activity", format("received activity result. request %s, result %s, isPaused %s, isCalculating %s", requestCode, resultCode,
	            isPaused, isCalculatingDistance));
		super.onActivityResult(requestCode, resultCode, data);
		switch(requestCode) {
		    case GPS_ENABLE_ACTIVITY_RESUME:
		        handleButtonClick(R.drawable.pause);
		        break;
		    case GPS_ENABLE_ACTIVITY_START:
		        handleButtonClick(R.drawable.button);
		        break;
		    case DISTANCE_CALC_PREF_CHANGE:
		        setDistanceParameters();
		        break;
		}
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}
	
    //onClickListener overrides
    @Override
    public void onClick(View v) {
        handleButtonClick(v.getId());
    }
    
    /**
     * processes button click based on which button is clicked and app's state 
     * 
     * @param buttonId button id which is clicked (or button click which is to be simulated)
     */
    private void handleButtonClick(int buttonId) {
        if (buttonId == R.drawable.button) {
            // for "start/stop calculating" button
            isCalculatingDistance = !isCalculatingDistance;
            if (isCalculatingDistance) {
                try {
                    locationService.start();
                    locationService.addListener(this);
                } catch(IllegalStateException e) {
                    Log.i("activity", "gps is not enabled, app can't be initialized");
                    isCalculatingDistance = !isCalculatingDistance;
                    showDialog(GPS_NOT_ENABLED_START);
                }
            } else {
                //when STOP is clicked
                SharedPreferences defPref = PreferenceManager.getDefaultSharedPreferences(this);
                boolean autoReport = defPref.getBoolean(DistanceCalculatorPrefActivity.PREF_KEY_AUTO_REPORT, false);
                Log.i("activity", "auto report is: " + autoReport);

                boolean autoSave = false;
                DistanceCalcReport report = null;
                if(autoReport) {
                    autoSave = defPref.getBoolean(DistanceCalculatorPrefActivity.PREF_KEY_AUTO_SAVE, false);
                    report = getSummaryReport();
                }
                Log.i("activity", "auto save is: " + autoSave);
                
                locationService.removeListener(this);
                locationService.stop();
                
                if(report != null) {
                    startReportActivity(report, autoSave);
                }
            }
            setActivityState();
        } else if (buttonId == R.drawable.pause && isCalculatingDistance) {
            // pause/resume only makes sense if system is calculating distance
            isPaused = !isPaused;
            Button pauseButton = (Button) findViewById(buttonId);
            if (isPaused) {
                locationService.pause();
                pauseButton.setText(R.string.resume);
            } else {
                try {
                    locationService.resume();
                    pauseButton.setText(R.string.pause);
                    errorText.setText(R.string.service_temp_not_available);
                } catch(IllegalStateException e) {
                    Log.i("activity", "gps is not enabled, app can't be started");
                    isPaused = !isPaused;
                    showDialog(GPS_NOT_ENABLED_RESUME);
                }
            }
        }
    }
    
    /**
     * starts the report activity for given report object. if given report object is null, error message
     * is displayed and nothing else is done
     * 
     * @param report object containing information required to display report activity
     * @param saveWhenRendered will save the report automatically when rendered fully on screen
     */
    private void startReportActivity(DistanceCalcReport report, boolean saveWhenRendered) {
        if (report == null) {
            Toast.makeText(this, getString(R.string.reportGenerationError), Toast.LENGTH_LONG).show();
            return;
        }
        Intent reportIntent = new Intent("action.distancecalculatorpaid.REPORT");
        reportIntent.putExtra(DistanceCalculatorReportActivity.INTENT_PARAM_AUTO_REPORT, report);
        reportIntent.putExtra(DistanceCalculatorReportActivity.INTENT_PARAM_AUTO_SAVE, saveWhenRendered);
        startActivity(reportIntent);
        Log.i("activity", "Generate report selected");
    }
    
    @Override
    protected Dialog onCreateDialog(int id) {
        Log.i("activity", format("creating dialog. id %s, isPaused %s, isCalculating %s", id, isPaused, isCalculatingDistance));
        Dialog dialog = null;
        switch (id) {
            case GPS_NOT_ENABLED_START:
                dialog = generateGpsDisabledAlert(GPS_ENABLE_ACTIVITY_START);                    
                break;
            case GPS_NOT_ENABLED_RESUME:
                dialog = generateGpsDisabledAlert(GPS_ENABLE_ACTIVITY_RESUME);
                break;
            default:
                dialog = super.onCreateDialog(id);
                break;
        }
        return dialog;
    }

    /**
     * method generates a dialog box to ask user to enable GPS for Distance Calculator to run
     * 
     * @return generated dialog box
     */
    private Dialog generateGpsDisabledAlert(final int requestCode) {
        Log.i("activity", "generating error box to enable GPS");
        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this);
        alertBuilder.setCancelable(true)
            .setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    startActivityForResult(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS), requestCode);
                    //startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
                }
            })
            .setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                }
            })
            .setMessage(getString(R.string.enableGpsDialogMsg));
        Log.i("activity", "gps disabled dialog created");
        return alertBuilder.create();
    }
    
    /**
     * obtains summary report object for this session. returns null if report can't be generated for some reason
     * 
     * @return report object for this session
     */
    private DistanceCalcReport getSummaryReport() {
        Log.i("mainactivity", "generating report");
        // obtains report object from service and populates strings as per user preferences
        DistanceCalcReport report = locationService.getSummaryReport();
        if (report == null) {
            return null;
        }
     
        report.setTotalDistanceString(DistanceCalculatorUtilities.getDistanceForReport(report.getTotalDistance(), multiplier, distanceSuffix));
        report.setTotalTimeString(DistanceCalculatorUtilities.getTimeForReport(report.getTotalTime()));
        report.setTotalTimePausedString(DistanceCalculatorUtilities.getTimeForReport(report.getTotalTimePaused()));
        report.setMinSpeedString(DistanceCalculatorUtilities.getSpeedForReport(report.getMinSpeed(), multiplier, distanceSuffix, hoursStr));
        report.setMaxSpeedString(DistanceCalculatorUtilities.getSpeedForReport(report.getMaxSpeed(), multiplier, distanceSuffix, hoursStr));
        report.setAvgSpeed(DistanceCalculatorUtilities.getAverageSpeedForReport(report.getTotalDistance(), report.getTotalTime(), multiplier,
                distanceSuffix, hoursStr));

        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
        report.setCurrentTime(sdf.format(System.currentTimeMillis()));
        return report;
    }
}